#include "DALCache.h"
#include "DALUtils.h"
#include "uthash.h"

#define UTCacheEntityHead (UTCacheEntity*)DALCache[EntityTypeIndex]

typedef struct
{
	int key;
	CacheEntity CacheEntity;
	UT_hash_handle hh;
} UTCacheEntity;

int CreateEntityCaches()
{
	unsigned int i;
	for (i = 0; i < EnvironmentEntityTable.Size; i++)
		DALCache[i] = NULL;

	return 0;
}

void DestructEntityCaches()
{
	unsigned int EntityTypeIndex;
	for (EntityTypeIndex = 0; EntityTypeIndex < EnvironmentEntityTable.Size; EntityTypeIndex++)
	{
		UTCacheEntity *current_entity, *tmp;

		HASH_ITER(hh, UTCacheEntityHead, current_entity, tmp)
		{
			HASH_DEL(UTCacheEntityHead, current_entity);
			DestructDataEntity(&(current_entity->CacheEntity.DataEntity));
			free(current_entity);
		}
		
	}
}

int CreateOrEditEntityInCache(unsigned int EntityTypeIndex, unsigned int ID, DataEntity DEntity)
{

	UTCacheEntity *this_entity;
	HASH_FIND_INT(UTCacheEntityHead, &ID, this_entity);

	//If we've found an entity, simply modify its underlying data and free the previous data
	if (this_entity)
		//Deallocate associated memory (data marked for delete should have been deallocated anyway)
		DestructDataEntity(&(this_entity->CacheEntity.DataEntity));
	//We have not found an entity at all, add a new one
	else
	{
		this_entity = (UTCacheEntity*)malloc(sizeof(UTCacheEntity));
		memset(this_entity, 0, sizeof(UTCacheEntity));
		this_entity->key = ID;

		HASH_ADD_INT(UTCacheEntityHead, key, this_entity);
	}

	//Whatever the cache entity was marked as before, now it should be CACHE_CREATE
	this_entity->CacheEntity.CacheType = CACHE_CREATE;

	//Either way we need to copy the data
	this_entity->CacheEntity.DataEntity = CopyDataEntity(&DEntity);

	return 0;
}

int SetEntityInCache(unsigned int EntityTypeIndex, unsigned int ID, DataEntity DEntity)
{
	//Try to see if that entity already exists

	UTCacheEntity *this_entity;
	HASH_FIND_INT(UTCacheEntityHead, &ID, this_entity);

	//If we've found an entity, this function should not be doing anything
	if (this_entity)
	{
		return 1;
	}
	//We have not found an entity at all, add a new one
	else
	{
		this_entity = (UTCacheEntity*)malloc(sizeof(UTCacheEntity));
		memset(this_entity, 0, sizeof(UTCacheEntity));
		this_entity->key = ID;

		HASH_ADD_INT(UTCacheEntityHead, key, this_entity);
	}

	//The cache entity needs to be marked as CACHE_SIMPLE
	this_entity->CacheEntity.CacheType = CACHE_SIMPLE;

	//We need to copy the data
	this_entity->CacheEntity.DataEntity = CopyDataEntity(&DEntity);

	return 0;
}

int DeleteEntityInCache(unsigned int EntityTypeIndex, unsigned int ID)
{
	int result = 1;
	//Try to see if that entity already exists

	UTCacheEntity *this_entity;
	HASH_FIND_INT(UTCacheEntityHead, &ID, this_entity);

	//If we've found an entity, free its data
	if (this_entity)
	{
		//This indicates that this item is already marked for delete, we need to indicate so
		if (this_entity->CacheEntity.CacheType == CACHE_DELETE)
			result = 0;

		//Deallocate associated memory (data marked for delete should have been deallocated anyway)
		DestructDataEntity(&(this_entity->CacheEntity.DataEntity));
	}
	//The entity was not found, but we still need to indicate that it is deleted, so we must add it to the cache as marked for delete
	else
	{
		this_entity = (UTCacheEntity*)malloc(sizeof(UTCacheEntity));
		memset(this_entity, 0, sizeof(UTCacheEntity));
		this_entity->key = ID;

		HASH_ADD_INT(UTCacheEntityHead, key, this_entity);
	}

	//Whatever the cache entity was marked as before, now it should be CACHE_DELETE
	this_entity->CacheEntity.CacheType = CACHE_DELETE;

	return result;
}

CacheEntity GetEntityInCache(unsigned int EntityTypeIndex, unsigned int ID)
{
	CacheEntity ce;
	//Try to see if that entity already exists
	UTCacheEntity *this_entity;
	HASH_FIND_INT(UTCacheEntityHead, &ID, this_entity);

	//The entity was found, return a copy of the cache entity
	if (this_entity)
	{
		ce = this_entity->CacheEntity;
		ce.DataEntity = CopyDataEntity(&(this_entity->CacheEntity.DataEntity));
	}
	//The entity was not found, return an empty CacheEntity
	else
	{
		ce.CacheType = CACHE_NOT_FOUND;
		ce.DataEntity.Data = NULL;
	}

	return ce;
}

